package com.zj.esproxy.config;

import com.zj.baseproxy.interfaces.BeanCreateRule;
import com.zj.baseproxy.utils.ClassUtil;
import com.zj.esproxy.interfaces.EsBaseMapper;
import com.zj.esproxy.interfaces.EsProxy;
import com.zj.esproxy.interfaces.IGetter;
import com.zj.esproxy.interfaces.impl.EsBaseMapperImpl;
import com.zj.esproxy.interfaces.impl.EsProxyImpl;
import com.zj.esproxy.model.EsInfo;
import com.zj.esproxy.utils.EsUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.modifier.FieldManifestation;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Objects;

/**
 * @author arthur_zhou
 * @date 2023/10/8 11:35
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(EsProperties.class)
public class EsConfig {


    @Bean("esBeanCreateRule")
    @ConditionalOnMissingBean(name = "esBeanCreateRule")
    public BeanCreateRule beanCreateRule(EsProperties esProperties, EsBaseMapperImpl esBaseMapperImpl) {
        if (StringUtils.isEmpty(esProperties.getUrl())) {
            log.warn("es路径缺失");
        }
        return new BeanCreateRule() {
            @Override
            public <T> T create(Class<T> clazz) {
                // 使用byte-buddy动态代理接口
                Object o = null;
                try {
                    DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition<Object> receiverTypeDefinition = new ByteBuddy().with(new NamingStrategy.AbstractBase() {
                                @Override
                                protected String name(TypeDescription typeDescription) {
                                    return "com.zj.esproxy.proxy." + clazz.getSimpleName() + "Impl";
                                }
                            }).subclass(Object.class)
                            .implement(clazz)
                            // 拦截除了Object的方法，也就是所有未实现的方法
                            .method(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class)))
                            .intercept(InvocationHandlerAdapter.of((o1, method, objects) -> null));
                    // 拦截通用模板方法
                    if (ClassUtil.isExtendsInterface(clazz, EsBaseMapper.class)) {
                        // 获取EsBaseMapper上的泛型class
                        Class<?>[] genericsClasses = ClassUtil.findGenericsClz(clazz, EsBaseMapper.class);
                        Class<?> genericsClass;
                        if (genericsClasses.length == 0 || Objects.isNull(genericsClass = genericsClasses[0])) {
                            genericsClass = Object.class;
                        }
                        // 组装es信息
                        EsInfo esInfo = EsUtil.getEsInfo(clazz);
                        esInfo.setUrl(esProperties.getUrl());
                        // 通用模板方法用通用拦截
                        receiverTypeDefinition = receiverTypeDefinition.method(ElementMatchers.isDeclaredBy(EsBaseMapper.class))
                                .intercept(MethodDelegation.to(esBaseMapperImpl))
                                // 定义一个属性private final Class typeClz; 为EsBaseMapper的泛型class
                                .defineField("typeClz", Class.class, Visibility.PRIVATE, FieldManifestation.FINAL)
                                // 定义一个属性private final String esInfo;
                                .defineField("esInfo", EsInfo.class, Visibility.PRIVATE, FieldManifestation.FINAL)
                                // 覆写构造方法,赋值final属性
                                .constructor(ElementMatchers.isDefaultConstructor())
                                .intercept(MethodCall.invoke(Object.class.getDeclaredConstructor())
                                        .andThen(FieldAccessor.ofField("typeClz").setsValue(genericsClass))
                                        .andThen(FieldAccessor.ofField("esInfo").setsValue(esInfo))
                                )
                                // 拦截get方法
                                .method(ElementMatchers.isDeclaredBy(IGetter.class))
                                .intercept(FieldAccessor.ofBeanProperty());
                    }
                    o = receiverTypeDefinition.make()
                            .load(clazz.getClassLoader())
                            .getLoaded()
                            .newInstance();
                } catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
                    log.warn("es bean生成失败", e);
                }
                return clazz.cast(o);
            }
        };
    }

    @Bean
    @ConditionalOnMissingBean(name = "esProxy")
    public EsProxy esProxy() {
        return new EsProxyImpl();
    }

    @Bean
    @ConditionalOnMissingBean(name = "esBaseMapperImpl")
    public EsBaseMapperImpl esBaseMapperImpl() {
        return new EsBaseMapperImpl();
    }

}
